#!/usr/bin/perl

# shadoworg.pl -- convert a ShadowPlan XML file to an emacs org-mode file
# Copyright (C) 2007, 2008 Jeff Stautz
#
# Author: Jeff Stautz <jeff dot stautz at gmail dot com>
# Keywords: ShadowPlan, org-mode, Palm, Emacs
# Homepage: jeffstautz dot com
# Version: 0.1
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

#
# shadoworg.pl converts a Shadow Plan XML file to an emacs org-mode text file
#
# It takes a Shadow Plan XML file as an argument and writes org-mode text
# to stdout. Output can be piped wherever you like.
#
# Typical usage looks something like this:
#    > shadoworg.pl ShadowP-MyShadowFile.xml > org_file.org
#
# DEPENDENCIES:
# To use this script, XML::Simple and Data::Dumper must be installed.
# You can find both on CPAN.
#
# TODO: Implement Target date and Start date parsing 


#--------------------------------------------------------------
# OPTIONS
#
# Edit the options below before running shadoworg.pl
#--------------------------------------------------------------

#--------------------------------------------------------------
# Odd_stars
# If your outline only uses odd-numbered *s, set this var to 1
#--------------------------------------------------------------
$odd_stars = 1;

#--------------------------------------------------------------
# Org startup options
# If you start your org-mode files with a particular header, put it here.
#--------------------------------------------------------------
$startup_string = "\n". "#+STARTUP: hidestars" . "\n" . "#+STARTUP: odd" 
  . "\n\n";

#--------------------------------------------------------------
# Completed keywords
# Set the keywords that "checked" items may have appended to their title
#
# Note: items completed in shadow will be marked "done" in org with the *first*
# keyword in this array.
#--------------------------------------------------------------
@completed_keywords = ('DONE');

#--------------------------------------------------------------
# Todo keywords
# An array of keywords that $completed_keyword will replace
#--------------------------------------------------------------
@todo_keywords = ('TODO');

#--------------------------------------------------------------
# Start, finish, target date keywords
# In order to change these dates, we'll need to know the keywords to look for
# If you won't be using a keyword, set it to ('NULL')
#--------------------------------------------------------------
@start_keywords = ('NULL');
@finish_keywords = ('CLOSED');
@target_keywords = ('SCHEDULED','DEADLINE');

#--------------------------------------------------------------
# Dealing with timestamps in headlines
# These mutually exclusive flags determine whether timestamps in a headline
# item are treated as target or start dates. If timestamps in a headline item
# have no bearing, then set both to 0.
#--------------------------------------------------------------
$headline_timestamp_is_target = 1;
$headline_timestamp_is_start = 0;

#--------------------------------------------------------------
# Set Weekdays
#--------------------------------------------------------------
@weekdays = qw(Sun Mon Tue Wed Thu Fri Sat);

#--------------------------------------------------------------
# End editable section
# Do not edit below
#--------------------------------------------------------------

use XML::Simple;
use Data::Dumper;

$xmlFile = shift or die "no arguments!";
$xml = new XML::Simple (KeyAttr=>'item', ForceArray=>1);
$data = $xml->XMLin($xmlFile);

$indent = '*';
$currentlevel = 1;

print $startup_string;
$output = dumpData($data);

sub dumpData($)
{
    $thedata = shift;
    foreach $item (@{$thedata->{item}})
    {
        my $title = $item->{title}[0];
		my $checked = $item->{checked};
		my $priority = $item->{priority};
		my $note = $item->{note}[0];
		my $target = $item->{hhTargetTime}[0];
		my $start = $item->{hhStartTime}[0];
		my $finish = $item->{hhFinishTime}[0];
		
        if ($checked  eq "yes")
		{
			if (!isCompleted($title))
			{
				$title = removeTodo($title);
				$note = changeFinishDate($note, $finish);
			}
			# otherwise, it's already completed in both places
		}
		print $indent . " " . $title, "\n";
		
		# if target time, check note for target time keyword and modify it
		if ($target)
		{
			#$note = changeTargetDate($note, $target);
		}
		
		#if start time, check note for start time keyword and modify it
		if ($start)
		{
			#$note = changeStartDate($note, $start);
		}
				
		if ($note ne '')
		{
			print $note;
		}
		
		if ($item->{item} ne '')
		{
			if ($odd_stars)
			{
				$indent = $indent . "**";
			}
			else
			{
				$indent = $indent . "*";
			}
			$currentlevel++;
			dumpData($item);
		}
		
    }
	
    $currentlevel--;
    if ($odd_stars)
    {
        $subst = $currentlevel*2 - 1;
        $indent = substr($indent, 0, $subst);
    }
    else
    {
        $indent = substr($indent, 0, $currentlevel);
    }
	
    return;
}

#--------------------------------------------------------------
# isCompleted function
#
#  Arguments: $title          string   title of the item
#
#  Returns:   $is_completed   int      1 if completed, 0 if not
#--------------------------------------------------------------
sub isCompleted($)
{
    $title = shift;
    $is_completed = 0;
    if (@completed_keywords ne NULL)
    {
		for $completed (@completed_keywords)
		{
			if ($title =~ /^$completed/)
			{
				$is_completed = 1;
			}
		}
    }
    return $is_completed;
}


#--------------------------------------------------------------
# removeTodo function
#
#  Arguments: $title      string   title of the item
#
#  Returns:   $thetitle   string   modified title, without todo keyword
#--------------------------------------------------------------
sub removeTodo($title)
{
	my $thetitle = shift;

    if (@todo_keywords ne NULL)
    {
		for $todo (@todo_keywords)
		{
			if ($thetitle =~ /$todo/)
			{
				$thetitle =~ s/$todo/$completed_keywords[0]/;
			}
		}
    }
    else
    {
        # if no todo, tack completion keyword on anyway
        $thetitle =~ s/\*\s/* $completed_keywords[0]/;
    }
	return $thetitle;
}


#--------------------------------------------------------------
# changeFinishDate function
#
# Add finish Date to note, using finish keyword and today's date
#
#  Arguments: $note        string   text of note for this item
#             $finishDate  int      date in Palm Epoch format
#
#  Returns:   $thenote     string   modified note with new finish date
#--------------------------------------------------------------
sub changeFinishDate($note, $finishDate)
{
    my $thenote = shift;
    my $finishDate = shift;
    my $myindent = '';
    my $date = '';

    # if hhFinishDate set, use that. if not, use current date
    if ($finishDate != 0 && $finishDate ne '')
    {
		$date = convertHHDate($finishDate);
    }
    else
    {
        $date = getCurrentDate();
    }
    if ($thenote =~ /^\s+/)
    {
        $myindent = $&;
    }
    $thenote = $myindent . $finish_keywords[0] . ": " . $date . "\n" . $thenote;
}


#--------------------------------------------------------------
# changeTargetDate function
#
# Not yet implemented since I don't use this.
#
#  Arguments: $note        string   text of note for this item
#             $target      int      date in Palm Epoch format
#
#  Returns:   $thenote     string   modified note with new target date
#--------------------------------------------------------------
sub changeTargetDate($note, $target)
{
# remember to check headline if flagged
# loop thru all target date keywords,
# go through whole note for each, search for keyword
# when keyword found, change date to target date

}


#--------------------------------------------------------------
# changeStartDate function
#
# Not yet implemented since I don't use this.
#
#  Arguments: $note        string   text of note for this item
#             $target      int      date in Palm Epoch format
#
#  Returns:   $thenote     string   modified note with new start date
#--------------------------------------------------------------
sub changeStartDate($note, $start)
{
# remember to check headline if flagged
# loop thru all start date keywords,
# go through whole note for each, search for keyword
# when keyword found, change date to date
}


#--------------------------------------------------------------
# getCurrentDate function
#
#  Arguments: none
#
#  Returns:   $datestring   string   date in org-mode format
#--------------------------------------------------------------
sub getCurrentDate()
{
    use POSIX;
    my $datestring = strftime( "%Y-%m-%d",localtime(time));

    $day = (localtime)[6];
    $day = $weekdays[$day];

    return "[" . $datestring . " " . $day . "]";
}


#--------------------------------------------------------------
# convertHHDate function
#
# Convert date from Palm Epoch format
#
#  Arguments: $finishDate  int      date in Palm Epoch format
#
#  Returns:   $datestring   string   date in org-mode format
#--------------------------------------------------------------
sub convertHHDate($finishDate)
{
    my $finishDate = shift;
    $finishDate = $finishDate - 2082844800;

    use POSIX;
    my $datestring = strftime( "%Y-%m-%d",localtime($finishDate));

    $day = (localtime($finishDate))[6];
    $day = $weekdays[$day];

    return "[" . $datestring . " " . $day . "]";
}
